package edu.pku.sei.mte.semantics;

import java.util.ArrayList;
import java.util.List;

import edu.pku.sei.mte.constants.SearchResult;
import edu.pku.sei.mte.exceptions.DeleteAfterCreateException;
import edu.pku.sei.mte.exceptions.InterruptException;
import edu.pku.sei.mte.exceptions.RetryException;
import edu.pku.sei.mte.modification.command.ModelModificationCommand;
import edu.pku.sei.mte.mtemodel.model.MClass;
import edu.pku.sei.mte.mtemodel.model.MModelGraph;
import edu.pku.sei.mte.mtemodel.model.MModelObject;
import edu.pku.sei.mte.mtemodel.mtmodel.ModelType;
import edu.pku.sei.mte.mtemodel.mtmodel.pattern.TPatternGraph;
import edu.pku.sei.mte.mtemodel.mtmodel.pattern.TPatternNode;
import edu.pku.sei.mte.mtemodel.mtmodel.rulebased.Domain;
import edu.pku.sei.mte.mtemodel.mtmodel.rulebased.Relation;
import edu.pku.sei.mte.runtime.Context;
import edu.pku.sei.mte.runtime.rulebased.RBTContext;
import edu.pku.sei.mte.runtime.rulebased.RBTEnvironment;

public class QVTSemantics extends Semantics {
	public static QVTSemantics INSTANCE = new QVTSemantics();
	
	private QVTSemantics(){}
	
	public boolean qvtCreateRelation(Relation relation,RBTEnvironment env,List<Context> unsatisfied) throws Exception{
		ModelType target = env.getTarget();
		Domain targetDomain = relation.getDomain(target);
		if(targetDomain==null)
			return true;

		MModelGraph targetModel = env.getModelGraph(target); 
		
		for(Context c : unsatisfied){
			boolean flag = false;
			do {
				ModelModificationCommand command = env.getModelModificationManager().getCurrentCommand();
				try{
					//enforce where
					enforceWhereClause(relation.getWhereClause(),c,true);
		
					createOrUpdate(targetDomain.getPattern(), c, targetModel);
					
					//enforce where
					if(enforceWhereClause(relation.getWhereClause(),c,false)==false){
						System.out.println("enforce error");
					} else {
						//record valid
						System.out.println(c);
						env.recordValidStateFrom(c);
						env.addTrace(c);
					}
					env.getExhandler().checkUserDefinedException();
				}catch(InterruptException e){
					env.getModelModificationManager().rollBackTo(command);
					break;
				}catch(RetryException e){
					env.getModelModificationManager().rollBackTo(command);
					if(flag)
						flag = false;
				}
			} while(flag);
		}
		return true;
	}
	
	public boolean checkSatisfied(Relation r, RBTContext c,boolean all) throws Exception {
		SearchResult result = findContext(r, c, null, null, true);
		if(all)
			return result == SearchResult.ALL_SATISFIED;
		else 
			return result == SearchResult.ALL_SATISFIED	|| result == SearchResult.PART_SATISFIED;
	}
	
	public boolean qvtDeleteRelation(Relation relation,RBTEnvironment env,RBTContext base) throws Exception{
		ModelType target = env.getTarget();
		Domain targetDomain = relation.getDomain(target);
		if(targetDomain==null)
			return true;
		
		MModelGraph targetModel = env.getModelGraph(target); 
		List<Context> targetMatch = match(targetDomain.getPattern(), targetModel, base);
		List<Context> mayBeDelete = new ArrayList<Context>();
		
		for(Context c : targetMatch){
			boolean flag = true;
			for(TPatternNode pn : targetDomain.getPattern().getNodes()){
				if(env.getModelValidState((MModelObject)c.getVariable(pn.getName()).getValue())==false){
					flag = false;
					break;
				}
			}
			if(flag){//all binding is valid, you can't delete any node
				continue;
			} else {
				mayBeDelete.add(c);
			}
		}
		for(Context c : mayBeDelete) {
			boolean del = checkSatisfied(relation, (RBTContext) c,false)==false;
			if(del){
				qvtDeleteObjectFromModel(targetDomain.getPattern(),env,c,targetModel);
			}
		}
		
		return true;
	}
	
	private void qvtDeleteObjectFromModel(TPatternGraph pattern,RBTEnvironment env,Context c,MModelGraph model) throws Exception{
		for(TPatternNode pn : pattern.getNodes()){
			MClass value = (MClass)c.getVariable(pn.getName()).getValue();
			if(env.getModelValidState(value)==false){
				if(env.isCreated(value)==false) {
						//model.removeNode(value);
					ModelModificationCommand.removeNodeRequest(model, value, c);
						env.setDeleted(value);
				} else {
					DeleteAfterCreateException ex = new DeleteAfterCreateException();
					ex.setContext(c);
					ex.setModelObject(value);
					
					env.getExhandler().catchSemanticException(ex);
					if(ex.isDelete()){
						//model.removeNode(value);
						ModelModificationCommand.removeNodeRequest(model, value, c);
						env.setDeleted(value);
					}
				}
			}
		}
	}
}
